
# -*- coding: utf-8 -*-
# SSHexec 打印结果文件

# 系统或第三方模块
import argparse
import sys
import os
import re
from typing import List, Dict, Any
from posixpath import join as posix_join


# 自定义模块
import src.config as config
from src.utils import elog


def format_terminal_output(result: Dict) -> str:
    """
    功能：
        格式化终端输出

    参数：
        result: 执行结果字典

    返回值：
        str: 格式化后的输出字符串
    """
    # 提取变量
    ip = result['ip']
    
    connect_bool = result['connect_bool']
    connect_str = result['connect_str'] if  result['connect_str'] else '连接异常信息没有正确截取'
    exit_bool = result['exit_bool']
    exit_status = result['exit_status']
    result_category = result['result_category'] if result['result_category'] else f'分类为空'
    elog.warning(f"[format_terminal_output]{ip} ，分类为空，请检查result_category数值来源")

    env = result['env']
    command = result['command'] if 'command' in result else result['script']  
    stdout = result['stdout']
    
    # thread_cost_time = result['thread_cost_time']
    shell_cost_time = result['shell_cost_time']

    elog.warning(f"[format_terminal_output]{ip} ，结果分类为：{result_category}")

    lines = []
    
    # 分割线
    lines.append(f"{'═' * 48}")

    # 第一行：连接状态
    if connect_bool:
        lines.append(f"【{ip}】 连接: 成功")
    else:
        lines.append(f"【{ip}】 连接: 异常")
        lines.append(f"【{ip}】 异常原因: {connect_str}")
        lines.append(f"【{ip}】 结果分类: {result_category}")
        # 如果连接失败，直接返回
        return "\n".join(lines)

    # 第二行：命令和环境变量
    lines.append(f'【{ip}】 执行: "{command}"' + (f' 环境变量: {env}' if env else ''))

    # 第三行：执行结果
    lines.append(f"【{ip}】 执行: {'成功' if exit_bool else '失败'}")

    # 第四行：标准输出
    lines.append(f"{stdout}")

    # 第五行：退出码
    lines.append(f"【{ip}】 退出码: {exit_status}")

    # 第六行：执行耗时
    # lines.append(f"【{ip}】 线程耗时: {thread_cost_time}秒")
    lines.append(f"【{ip}】 耗时: {shell_cost_time}秒")
    
    # 第七行：结果分类
    lines.append(f"【{ip}】 结果分类: {result_category}")

    
    debug = False
    # 添加调试信息
    if debug:
        lines.append(f"=== 以下为调试信息 ===")
        lines.append(f"{ip}】 本地开始时间: {result['exec_start_time']}")
        lines.append(f"{ip}】 本地结束时间: {result['exec_end_time']}")
        lines.append(f"{ip}】 本地开始时间: {result['exec_start_time']}")
        lines.append(f"{ip}】 本地结束时间: {result['exec_end_time']}")
        lines.append(f"{ip}】 连接超时: {result['connect_timeout']}")
        lines.append(f"{ip}】 执行超时: {result['exec_timeout']}")
        lines.append(f"{ip}】 环境变量: {env}")
        lines.append(f"{ip}】 本地开始时间: {result['exec_start_time']}")
    # 字符串的列表 用\n连接，并返回连接后的字符串
    return "\n".join(lines)


def format_output_to_xlsx(log_dir: str) -> None:
    """
    功能：
        格式化output.txt文件内容到Excel文件
    
    参数:
        log_dir: 日志目录路径
        
    返回:
        None
    """
    from openpyxl import Workbook
    from openpyxl.styles import Font, Alignment, PatternFill
    from openpyxl.utils import get_column_letter
    from openpyxl.worksheet.worksheet import Worksheet

    # 如果output.txt不存在，直接返回
    output_file = posix_join(log_dir, config.DEFAULT_FILE_OUTPUT)
    if not os.path.exists(output_file):
        return

    # 读取日志内容
    try:
        with open(output_file, 'r', encoding='utf-8') as f:
            log_content = f.read()
            
    except Exception as e:
        print(f"{config.COLOR_RED}[ERROR]{config.COLOR_RESET}{config.COLOR_YELLOW}[function:format_output_to_xlsx]{config.COLOR_RESET}读取日志文件失败: {e}", file=sys.stderr)
        return

    # 配置输出路径
    os.makedirs(log_dir, exist_ok=True)
    output_path = posix_join(log_dir, config.DEFAULT_FILE_OUTPUT_XLSX)

    # 创建Excel工作簿
    try:
        wb = Workbook()
        if wb.active is None:
            raise RuntimeError("Failed to create workbook with active sheet")
        
        ws: Worksheet = wb.active
        ws.title = "执行日志"
    except Exception as e:
        print(f"创建工作簿失败: {e}", file=sys.stderr)
        return

    # 设置样式
    header_font = Font(bold=True, color="FFFFFF", size=12)
    header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
    header_alignment = Alignment(horizontal="center", vertical="center")

    # 写入表头
    headers = ["IP地址", "事件类型", "内容详情"]
    for col_idx, header in enumerate(headers, 1):
        cell = ws.cell(row=1, column=col_idx, value=header)
        if cell:  # 确保cell对象存在
            cell.font = header_font
            cell.fill = header_fill
            cell.alignment = header_alignment

    # 处理日志内容
    current_ip = ""
    row_idx = 2
    lines = log_content.split('\n')

    for line in lines:
        line = line.strip()
        if not line:
            continue

        # 处理══════行，转换为空白行
        if line.startswith('══════'):
            row_idx += 1
            continue

        # 匹配IP行
        ip_match = re.search(r'【(\d+\.\d+\.\d+\.\d+)】', line)
        if ip_match:
            current_ip = ip_match.group(1)
            content_start = line.find('】') + 1
            content = line[content_start:].strip()

            # 写入主记录
            ws.cell(row=row_idx, column=1, value=current_ip)
            ws.cell(row=row_idx, column=2, value=content)
            row_idx += 1
        elif current_ip:
            # 作为标准输出和错误输出内容
            ws.cell(row=row_idx, column=1, value=current_ip)
            ws.cell(row=row_idx, column=2, value="标准输出和错误输出")
            cell_content = ws.cell(row=row_idx, column=3, value=line.strip())
            
            # 设置内容单元格自动换行
            if cell_content:
                cell_content.alignment = Alignment(vertical="top", wrap_text=True)
            row_idx += 1

    # 设置列宽
    column_widths = [15, 20, 60]
    for col_idx, width in enumerate(column_widths, 1):
        col_letter = get_column_letter(col_idx)
        if hasattr(ws, 'column_dimensions'):
            ws.column_dimensions[col_letter].width = width

    # 冻结首行
    if hasattr(ws, 'freeze_panes'):
        ws.freeze_panes = "A2"

    # 添加筛选
    if hasattr(ws, 'auto_filter'):
        ws.auto_filter.ref = f"A1:{get_column_letter(len(headers))}{row_idx-1}"

    # 保存文件
    try:
        wb.save(output_path)
        return
    except Exception as e:
        print(f"{config.COLOR_RED}[ERROR]{config.COLOR_RESET}{config.COLOR_YELLOW}[function:format_output_to_xlsx]{config.COLOR_RESET}保存Excel文件失败: {e}", file=sys.stderr)
        return
    finally:
        wb.close()



def format_dict_list_to_xlsx(final_results: List[Dict[str, Any]], log_dir: str) -> None:
    """
    功能：
        格式化字典列表为xlsx文件
    
    参数：
        final_results: 字典列表
        log_dir: 日志目录路径
        
    返回值：
        None
    """
    from openpyxl import Workbook
    from openpyxl.styles import Font, Alignment, Border, Side, PatternFill
    from openpyxl.utils import get_column_letter

    if not final_results:
        print(f"{config.COLOR_YELLOW}结果字典列表为空，未生成{config.DEFAULT_FILE_RESULTS}{config.COLOR_RESET}")
        return
    
    # 生成输出路径
    os.makedirs(log_dir, exist_ok=True)
    dict_path = posix_join(log_dir, config.DEFAULT_FILE_RESULTS)
    
    wb = None
    try:
        # 创建新工作簿
        wb = Workbook()
        if wb.active is None:
            raise RuntimeError("创建工作簿失败")
            
        ws = wb.active
        ws.title = "Results"
        
        # 基础样式配置
        header_font = Font(bold=True, color="FFFFFF")
        header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
        header_alignment = Alignment(horizontal="center", vertical="center")
        border = Border(left=Side(style='thin'), right=Side(style='thin'),
                       top=Side(style='thin'), bottom=Side(style='thin'))
        
        # 写入表头
        if not final_results:
            raise ValueError("Empty input data")
            
        headers = list(final_results[0].keys())
        for col_idx, header in enumerate(headers, 1):
            cell = ws.cell(row=1, column=col_idx, value=header)
            cell.font = header_font
            cell.fill = header_fill
            cell.alignment = header_alignment
            cell.border = border
        
        # 写入数据并计算列宽
        max_lengths = {header: len(header) for header in headers}
        
        for row_idx, row_data in enumerate(final_results, 2):
            for col_idx, header in enumerate(headers, 1):
                value = row_data.get(header, "")
                cell = ws.cell(row=row_idx, column=col_idx, value=value)
                if cell:  # 确保cell创建成功
                    cell.border = border
                    str_value = str(value)
                    if len(str_value) > max_lengths[header]:
                        max_lengths[header] = len(str_value)
        
        # 调整列宽
        for col_idx, header in enumerate(headers, 1):
            column_letter = get_column_letter(col_idx)
            if hasattr(ws, 'column_dimensions'):
                adjusted_width = min(max_lengths[header] + 2, 50)
                ws.column_dimensions[column_letter].width = adjusted_width
        
        # 添加自动筛选（如果工作表有效）
        if hasattr(ws, 'auto_filter'):
            last_col = get_column_letter(len(headers))
            last_row = len(final_results) + 1
            ws.auto_filter.ref = f"A1:{last_col}{last_row}"
        
        wb.save(dict_path)
        
    except PermissionError as pe:
        print(f"{getattr(config, 'COLOR_RED', '')}[ERROR]{getattr(config, 'COLOR_RESET', '')} "
              f"无法写入文件，请检查权限: {dict_path}", file=sys.stderr)
    except Exception as e:
        print(f"{getattr(config, 'COLOR_RED', '')}[ERROR]{getattr(config, 'COLOR_RESET', '')} "
              f"输出xlsx文件时出错: {str(e)}", file=sys.stderr)
    finally:
        if wb is not None:
            wb.close()


def format_statistic_results_to_terminal(results_statistic: dict) -> None:
    """
    功能：
        格式化统计结果信息输出到终端
    
    参数：
        results_statistic: 结果统计信息字典
    
    返回值：
        None
    """

    # print(f'\n')
    print('═' * 48)
    print(f"  执行耗时： {results_statistic['global_cost_time']}  秒")
    if results_statistic['verify'] == '通过':
        print(f"  {config.COLOR_CYAN}节点总数:{config.COLOR_RESET} {results_statistic['nodeinofs_total']}  {config.COLOR_CYAN}完成总数：{config.COLOR_RESET}{results_statistic['results_total']}")
    else:
        print(f"  {config.COLOR_CYAN}节点总数:{config.COLOR_RESET} {results_statistic['nodeinofs_total']}  {config.COLOR_CYAN}完成总数：{config.COLOR_RESET}{results_statistic['results_total']}  {config.COLOR_CYAN}总数校验：{config.COLOR_RESET}{results_statistic['verify_color']}{results_statistic['verify']}{config.COLOR_RESET}")
    print(f"  {config.COLOR_GREEN}成功:{config.COLOR_RESET} {results_statistic['success_counts']}   {config.COLOR_RED}失败:{config.COLOR_RESET} {results_statistic['fail_counts']}")
    
    if results_statistic['sorted_fail_categories']:
        print(f'  {config.COLOR_RED}失败分类统计{config.COLOR_RESET} >>>  {"  ".join(f"{config.COLOR_YELLOW}{k}：{config.COLOR_RESET}{v}" for k, v in results_statistic["sorted_fail_categories"])}')
    print('═' * 48)
    
    return

def format_statistic_results_to_report(results_statistic: dict, log_dir: str, args: argparse.Namespace) -> None:
    """
    功能：
        格式化统计结果信息输出到报告文件
    
    参数：
        results_statistic: 结果统计信息字典
        log_dir: 日志目录路径
        args: 命令行参数

    返回值：
        None
    """
    report_file = posix_join(log_dir, config.DEFAULT_FILE_REPORT)
    
    # 格式化执行命令内容
    args_set = sys.argv[1:]
    args_content = " ".join(args_set) if args_set else ""
    command = f"python {sys.argv[0]} {args_content}"
    
    
    with open(report_file, 'w', encoding='utf-8') as f:
        f.write('=============================执行结果统计报告=============================\n')
        f.write(f'执行开始时间： {results_statistic["global_start_time"]}\n')
        f.write(f'执行结束时间： {results_statistic["global_stop_time"]}\n')
        f.write(f'执行耗时： {results_statistic["global_cost_time"]}  秒\n')    
        f.write(f'\n【执行命令】 \n  {command}\n')
        f.write(f'\n【执行参数】\n')
        if args.c:
            f.write(f'  执行模式： 命令模式\n')
            f.write(f'  执行命令： {args.c}\n')
        if args.s:
            f.write(f'  执行模式： 脚本模式\n')
            f.write(f'  脚本路径： {args.s}\n')
        if args.e:
            f.write(f'  环境变量： {args.e}\n')
        if args.u:
            f.write(f'  执行模式： 上传模式\n')
            f.write(f'  本地路径： {args.u}\n')
            f.write(f'  远程路径： {args.p}\n')
        if args.d:
            f.write(f'  执行模式： 下载模式\n')
            f.write(f'  远程路径： {args.d}\n')
            f.write(f'  本地路径： {args.p}\n')

        f.write(f'  CSV文件路径： {args.f}\n')
        f.write(f'  节点数量： {results_statistic["nodeinofs_total"]}\n')
        if args.c or args.s:
            f.write(f'  并发数值： {args.n}\n')
        if args.T:
            f.write(f'  连接超时： {args.T}s\n')
        if args.t:
            if args.c or args.s:
                f.write(f'  执行超时： {args.t}s\n')
            if args.u or args.d:
                f.write(f'  传输超时： {args.t}s\n')
        
        f.write(f'\n【结果统计】\n')
        f.write(f"  执行耗时： {results_statistic['global_cost_time']}  秒\n")
        f.write(f"  节点总数: {results_statistic['nodeinofs_total']}  完成总数：{results_statistic['results_total']}  总数校验：{results_statistic['verify']}\n")
        f.write(f"  成功: {results_statistic['success_counts']}    失败: {results_statistic['fail_counts']}\n")
        
        if results_statistic['sorted_fail_categories']:
            f.write(f'  失败分类统计 -→  {"  ".join(f"{k}：{v}" for k, v in results_statistic["sorted_fail_categories"])}\n')
        
        f.write(f'\n【IP清单统计】\n')
        
        # 先输出失败分类（按IP数量升序排列）
        sorted_fail_items = sorted(results_statistic['category_ip_map'].items(), key=lambda x: len(x[1]))
        for category, ips in sorted_fail_items:
            f.write(f'\n{category}（{len(ips)}）：\n')
            for ip in ips:
                f.write(f'{ip}\n')
        
        # 最后输出成功分类
        if results_statistic['sorted_success_ips']:
            f.write(f'\n{results_statistic["success_category"]}（{results_statistic["success_ips_count"]}）：\n')
            for ip in results_statistic['sorted_success_ips']:
                f.write(f'{ip}\n')
